/*
 * Building.cpp
 *
 *  Created on: 22/04/2012
 *      Author: alex
 */

#include "Building.h"

using namespace std;

Building::Building(int floorAmmount, float elevatorSpeed, float elevatorMaxCapacity,
					int elevatorAmmount,std::string fileName, std::string keyGenerationPath):
					m_floors(floorAmmount, keyGenerationPath, fileName+"floor"),
					m_elevators(floorAmmount,elevatorAmmount,elevatorSpeed,elevatorMaxCapacity,
							fileName+"floor", keyGenerationPath),
					m_doRun(true),
					m_elevatorCalled(false),
					m_keyGenerationPath(keyGenerationPath)

{
	srand ( time(NULL));
}

Building::~Building()
{

}

void Building::stopSimulation()
{
	m_doRun = false;
    m_floors.stopGeneratingPassengers();
    m_elevators.stop();
    m_floors.stop();
}

void Building::startSimulation(int simulationDuration)
{
	m_SimulationDuration = simulationDuration;
    m_floors.start(true);
    m_elevators.start();

	FloorIdentifierList callingFloors;

	m_SimulationStart = time(NULL);

    while(m_doRun)
    {
    	callingFloors = m_floors.getWaitingFloors();

    	distributeElevatorCalls(callingFloors);

//    	//TODO remove temp
//    	sleep(1);

		//Si paso el tiempo de la simulacion dejo de aceptar pasajeros
		if((time(NULL) >= m_SimulationDuration + m_SimulationStart))
		{
			CLogger::log("Simulation time ended, waiting for passengers to unload",
					LogLevelEnum::DEBUG);
			stopSimulation();
		}
    }
}

void Building::distributeElevatorCalls(FloorIdentifierList callingFloors)
{
	vector<FloorIdentifierList> assignedCalls(m_elevators.getElevatorAmmount());

	FloorIdentifierList::iterator idIt;
	ElevatorStatus elevatorStatus;
	int i;
	ElevatorIdentifier elevatorId;

	//Limpio la lista de pedidos apra cada piso antes de arrancar
	for(i=0; i <m_elevators.getElevatorAmmount(); i++)
		assignedCalls[i].clear();

	//Recorro la lista de llamadas y las asigno a los distintos ascensores
	for(idIt = callingFloors.begin(); idIt != callingFloors.end(); idIt++)
	{
		elevatorId = getElevatorForCall(*idIt);

		if(elevatorId != UNDEFINED)
			assignedCalls[elevatorId].push_back(*idIt);
	}

	//Setea la lista de destinos en cada ascensor
	for(i=0; i <m_elevators.getElevatorAmmount(); i++)
	{
		if(assignedCalls[i].size()> 0)
		{
			m_elevators.setHallCalls(i,assignedCalls[i]);

			elevatorStatus = m_elevators.getElevatorStatus(i);
			elevatorStatus.hasNewCalls = true;
			m_elevators.setElevatorStatus(elevatorStatus,i);
//	    	printIdentifierList(assignedCalls[i]);
		}
	}
}

ElevatorIdentifier Building::getElevatorForCall(FloorIdentifier floorId)
{
	int i;
	int elevatorId = UNDEFINED;
	bool isFloorAsigned= false;
	bool isElevatorAvailable= false;
	FloorIdentifierList floorList;
	ElevatorStatus elevatorStatus;
	int elevatorIndex;
	vector<ElevatorIdentifier> suitableElevators;

	//Veo si el pedido no esta ya asignado
	for(i=0; i <m_elevators.getElevatorAmmount() && !isFloorAsigned; i++)
	{
		floorList = m_elevators.getAllCalls(i);

		if(find(floorList.begin(),  floorList.end(), floorId) != floorList.end())
			isFloorAsigned = true;
	}

	//Si no fue asignado todavia
	if(!isFloorAsigned)
	{
		for(i=0; i <m_elevators.getElevatorAmmount(); i++)
		{
			elevatorStatus = m_elevators.getElevatorStatus(i);

			if(elevatorStatus.hasNewCalls != true)
			{
				//Marco el ascensor como disponible dependiendo
				//la dir de movimiento y el piso en el que se encuentra
				//el ascensor
				switch(elevatorStatus.m_currentDirection)
				{
				case GOING_UP:
					if(floorId >=elevatorStatus.m_currentFloor)
						isElevatorAvailable = true;
					break;
				case GOING_DOWN:
					if(floorId <=elevatorStatus.m_currentFloor)
						isElevatorAvailable = true;
					break;
				case STOPPED:
						isElevatorAvailable = true;
					break;
				}

				//Si puede responder el pedido
				if(isElevatorAvailable)
				{
					suitableElevators.push_back(i);
					isElevatorAvailable = false;
				}
			}
		}

		//Si hay ascensores que puedan responder el pedido, elijo uno al azar
		if(suitableElevators.size()> 0)
		{
			elevatorIndex = ((float)rand()/ (float) RAND_MAX) * suitableElevators.size();
			elevatorId =suitableElevators[elevatorIndex];
		}
	}

	return elevatorId;
}
